package com.jzo2o.market.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.CurrentUserInfo;
import com.jzo2o.common.utils.*;
import com.jzo2o.market.constants.RedisConstants;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.mapper.CouponMapper;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.dto.request.CouponOperationPageQueryReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.model.dto.response.CouponQueryForPageResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.market.service.ICouponUseBackService;
import com.jzo2o.market.service.ICouponWriteOffService;
import com.jzo2o.market.utils.CouponUtils;
import com.jzo2o.mvc.utils.UserContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-09-16
 */
@Service
@Slf4j
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {

    @Resource(name = "seizeCouponScript")
    private DefaultRedisScript<String> seizeCouponScript;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private IActivityService activityService;

    @Resource
    private ICouponUseBackService couponUseBackService;

    @Resource
    private ICouponWriteOffService couponWriteOffService;


    /**
     * 分页查询优惠卷
     *
     * @return
     */
    @Override
    public CouponQueryForPageResDTO queryPage(CouponOperationPageQueryReqDTO dto) {
        if (ObjectUtils.isEmpty(dto.getActivityId())){
            throw new ForbiddenOperationException("请先选择活动");
        }
        Page page = new Page(dto.getPageNo(), dto.getPageSize());

        Page pageQuery = this.lambdaQuery()
                .eq(Coupon::getActivityId, dto.getActivityId())
                .orderByDesc(Coupon::getCreateTime)
                .page(page);

        CouponQueryForPageResDTO couponQueryForPageResDTO = new CouponQueryForPageResDTO();
        couponQueryForPageResDTO.setList(pageQuery.getRecords());
        couponQueryForPageResDTO.setTotal(pageQuery.getTotal());
        couponQueryForPageResDTO.setPages(pageQuery.getPages());
        return couponQueryForPageResDTO;
    }


    /**
     * 查询优惠卷列表
     *
     * @param status
     * @param lastId
     * @return
     */
    @Override
    public List<Coupon> queryCouponList(Integer status, Long lastId) {
        CurrentUserInfo currentUserInfo = UserContext.currentUser();
        if (ObjectUtils.isNotEmpty(currentUserInfo)){
            throw new ForbiddenOperationException("请先登录");
        }
        List<Coupon> list = this.lambdaQuery()
                .eq(Coupon::getUserId, currentUserInfo.getId())
                .eq(Coupon::getStatus, status)
                .lt(ObjectUtils.isNotEmpty(lastId), Coupon::getId, lastId)
                .orderByDesc(Coupon::getId, Coupon::getCreateTime)
                .last("limit 5")
                .list();

        return list;
    }

    /**
     * 领取优惠卷
     *
     * @param id
     */
    @Override
    public void seizeCoupon(Long id) {
        //获取活动
        ActivityInfoResDTO activity = this.queryActivityInfoForCache(id);
        if (ObjectUtils.isEmpty(activity)){
            throw new ForbiddenOperationException("活动不存在");
        }
        //校验活动状态
        Integer realStatus = activityService.getRealStatus(activity);
        if (ObjectUtils.notEqual(realStatus, ActivityStatusEnum.DISTRIBUTING.getStatus())){
            throw new ForbiddenOperationException("非法活动");
        }
        //校验活动是否过期
        if (DateUtils.now().isBefore(activity.getDistributeStartTime()) || DateUtils.now().isAfter(activity.getDistributeEndTime())){
            throw new ForbiddenOperationException("不在活动时间");
        }

        //构造lua脚本参数
        Long activityId = id; //活动id
        Long userId = UserContext.currentUserId(); //用户id
        Long index = activityId % 10; //活动id对10取余的值
        List<String> keys = Arrays.asList(
                //KEYS[1] : 抢券同步队列的RedisKey
                String.format(RedisConstants.RedisKey.COUPON_SEIZE_SYNC_QUEUE_NAME, index),
                //KEYS[2] : 活动库存的RedisKey
                String.format(RedisConstants.RedisKey.COUPON_RESOURCE_STOCK, index),
                //KEYS[3] : 抢券成功队列的RedisKey
                String.format(RedisConstants.RedisKey.COUPON_SEIZE_LIST, activityId, index)
        );
        //执行lua脚本
        Object result = redisTemplate.execute(seizeCouponScript, keys, activityId, userId);
        if (ObjectUtils.isEmpty(result)){
            throw  new ForbiddenOperationException("lua脚本执行异常");
        }

        //判断返回值
        Long code = Long.valueOf(result.toString());
        if(code>0) {
            return;
        }

        if(code==-1) {
            throw new CommonException("已领取过，不要重复领取");
        }

        if(code==-2 || code==-4) {
            throw new CommonException("已抢光");
        }

        if(code==-3 || code==-5) {
            throw new CommonException("抢券失败");
        }
    }

    /**
     * 从缓存查询活动信息
     * @param id
     * @return
     */
    public ActivityInfoResDTO queryActivityInfoForCache(Long id){
        if (ObjectUtils.isEmpty(id)){
            return null;
        }
        Object jsonStr = redisTemplate.opsForValue().get(RedisConstants.RedisKey.ACTIVITY_CACHE_LIST);
        if (ObjectUtils.isEmpty(jsonStr)){
            return null;
        }
        List<ActivityInfoResDTO> list = JsonUtils.toList(jsonStr.toString(), ActivityInfoResDTO.class);
        ActivityInfoResDTO activityInfoResDTO = list.stream().filter(x -> ObjectUtils.equals(id, x.getId())).findFirst().orElse(null);
        return activityInfoResDTO;
    }

    /**
     * 查询可用优惠卷
     *
     * @param totalAmount
     * @return
     */
    @Override
    public List<AvailableCouponsResDTO> getAvailable(BigDecimal totalAmount) {

        List<Coupon> couponList = this.lambdaQuery()
                .eq(Coupon::getUserId, UserContext.currentUserId())//当前用户id
                .eq(Coupon::getStatus, CouponStatusEnum.NO_USE.getStatus())//未使用
                .le(Coupon::getAmountCondition, totalAmount)//满减金额小于等于订单金额
                .lt(Coupon::getDiscountAmount, totalAmount)//优惠金额小于订单金额
                .gt(Coupon::getValidityTime, DateUtils.now())//有效期大于当前时间
                .list();

        if (ObjectUtils.isEmpty(couponList)){
            return CollUtils.emptyList();
        }

        List<AvailableCouponsResDTO> availableCouponsResDTOS = couponList.stream()
                .peek(x -> x.setDiscountAmount(CouponUtils.calDiscountAmount(x, totalAmount)))//计算优惠金额
                .filter(x -> x.getDiscountAmount().compareTo(BigDecimal.ZERO) > 0 && x.getAmountCondition().compareTo(totalAmount) <= 0)//优惠金额大于0且满减金额小于等于订单金额
                .map(x -> BeanUtils.copyBean(x, AvailableCouponsResDTO.class))//构建返回值类型
                .sorted(Comparator.comparing(AvailableCouponsResDTO::getDiscountAmount).reversed())//根据优惠金额倒序排序
                .collect(Collectors.toList());

        return availableCouponsResDTOS;
    }
}
